package com.jintian.smart.kernel.autoconfigure.model;

import com.jintian.smart.kernel.autoconfigure.beetl.BeetlSqlBeanDefinitionRegistrar;
import com.jintian.smart.kernel.autoconfigure.beetl.DataSourceNacosBeanRegistryProcessor;
import com.jintian.smart.kernel.autoconfigure.extend.util.ClassUtil;
import com.jintian.smart.kernel.common.expr.DefaultFunctionRepository;
import com.jintian.smart.kernel.common.orm.RestResultResponseBodyAdvice;
import com.jintian.smart.kernel.common.orm.RestResultWrapperReturnValueHandler;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.annotation.expr.ExprFn;
import com.jintian.smart.kernel.core.constant.ModelConst;
import com.jintian.smart.kernel.core.expr.IExprCalcContext;
import com.jintian.smart.kernel.core.expr.IExpressionCalc;
import com.jintian.smart.kernel.core.expr.IFunctionRepository;
import com.jintian.smart.kernel.core.expr.IFunctionResolver;
import com.jintian.smart.kernel.core.web.DefaultRestResultBuilder;
import com.jintian.smart.kernel.core.web.RestResultBuilder;
import com.jintian.smart.kernel.interceptor.MultiApplicationContextInterceptor;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.Context;
import org.beetl.core.Function;
import org.beetl.core.GroupTemplate;
import org.beetl.ext.spring6.BeetlGroupUtilConfiguration;
import org.beetl.ext.spring6.BeetlSpringView;
import org.beetl.ext.spring6.BeetlSpringViewResolver;
import org.beetl.sql.core.ConditionalSQLManager;
import org.beetl.sql.core.ExecuteContext;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.engine.template.BeetlTemplateEngine;
import org.beetl.sql.ext.Slf4JLogInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

// 主环境,从Nacos装配数据源
@Slf4j
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = ModelConst.PROPERTY_SMART_MASTER, havingValue = "true", matchIfMissing = true)
@Import(DataSourceNacosBeanRegistryProcessor.class)
public class MasterApplicationContext {
    static final Map<String, Class<?>> innerExpressionCalcImpl = new HashMap<>();

    static {
        innerExpressionCalcImpl.put("IK", ClassUtil.convertClassNamesToClasses(Arrays.asList("com.jintian.smart.kernel.common.expr.IKExpressionCalc")).get(0));
        innerExpressionCalcImpl.put("SPEL", ClassUtil.convertClassNamesToClasses(Arrays.asList("com.jintian.smart.kernel.common.expr.SpelExpressionCalc")).get(0));
    }

    @Bean
    ModelManager modelManager() {
        return new ModelManager();
    }

    // TODO 未集成ORM，临时注释
//        @Bean
//        CodeGenController codeGenController() {
//            return new CodeGenController();
//        }

    @ConditionalOnMissingBean(name = "defaultWrapper", value = RestResultBuilder.class)
    @Bean(name = "defaultWrapper")
    RestResultBuilder restResultBuilder() {
        return new DefaultRestResultBuilder();
    }

    /**
     * 封装响应
     *
     * @return
     */
    @Bean
    RestResultResponseBodyAdvice restResultResponseBodyAdvice() {
        return new RestResultResponseBodyAdvice();
    }

    @Bean
    RestResultWrapperReturnValueHandler restResultWrapperReturnValueHandler() {
        return new RestResultWrapperReturnValueHandler();
    }

    @ConditionalOnMissingBean(IFunctionRepository.class)
    @Bean
    IFunctionRepository functionRepository() throws Exception {
        return new DefaultFunctionRepository();
    }

    @ConditionalOnMissingBean(IExpressionCalc.class)
    @Bean
    IExpressionCalc expressionCalc(@Value("${smart.exprEngine.type:SPEL}") String type,
                                   @Autowired(required = false) IFunctionRepository repository,
                                   ObjectProvider<IFunctionResolver> resolver) throws Exception {
        Class<?> clazz = innerExpressionCalcImpl.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("不存在表达式计算器" + type);
        }
        IExpressionCalc IExpressionCalc = IExpressionCalc.class.cast(clazz.newInstance());
        if (repository != null) {
            resolver.forEach(r -> r.custom(repository));
            IExpressionCalc.setFunctionRepository(repository);
        }
        return IExpressionCalc.class.cast(clazz.newInstance());
    }

    @ConditionalOnMissingBean(IExprCalcContext.class)
    IExprCalcContext defaultExprCalcContext() {
        return new IExprCalcContext() {
            @Override
            public Map<String, Object> getDefaultContextVariables() {
                return new HashMap<>();
            }
        };
    }

    /**
     * 注册HandlerInterceptor，线程保持ApplicationContext及ClassLoader
     *
     * @return
     */
    @Bean
    public WebMvcConfigurer classLoaderConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                // 多ApplicationContext的拦截器
                registry.addInterceptor(new MultiApplicationContextInterceptor());
            }
        };
    }
}
